package search;

public class TranspositionTable {
	//TRANSPOSITION TABLE
	public static boolean useTT=true;
    private static final int capacity=4194304;
	private static final long hashMask = 0x7fffffffffffffffl;
	public static final int GAME_MOVE_DEPTH = 999;
	
	private static long[] hashValue;
	private static  int[] bestmove;
	private static int[]  depthValue;
	private static char[]  flags;
	private static int[]  nodeScore;
	
	
	
	public static int LOWERBOUND=0;
	public static int UPPERBOUND=1;

	public static int probes=0, hits=0, stores=0, usefulScores=0, repetitionsFound=0;
	
	
/** The number of items in the hashtable  */
	
	static {
		if (useTT) initTanspositionTable();
	}
	// END TRANS TABLE
	public static void initTanspositionTable()
	{
		hashValue =         new long[capacity];
		bestmove     =      new int[capacity];
		depthValue =        new int[capacity];
		flags=              new char[capacity];
		nodeScore=          new int[capacity];
	}
	public static int getDepth(long hash2) {
		int index2=index(hash2);
		return depthValue[index2];
	}
	public static int getScore(long hash2) {
		int index2=index(hash2);
		return nodeScore[index2];
	}
	public static int getMove(long hash2) {
		int index2=index(hash2);
		return bestmove[index2];
	}
	private static int index(long hash)
	{
		// mask to make sure it is a positive value
		hash &= hashMask;
		return (int)(hash % capacity);

	}
	public static boolean  probeHash(long hash2)
	{
		int index2=index(hash2);
		probes++;
	 
		if (hashValue[index2] == hash2 ) {
				hits++;
	            return true;
	    }
	    return false;
	}
	public static boolean isRepetition(long hash2) {
		int index2=index(hash2);
		if (depthValue[index2] == GAME_MOVE_DEPTH)  {
			return true;
		}
		return false;
	}
	public static void hashStore(long hash2, int depth2, int move,  int score, int alpha, int beta )
	{
		char boundType = 0;
		
		int index2=index(hash2);
		
		if (depthValue[index2] == GAME_MOVE_DEPTH)  {
			//this is a move played in the actual game, we are concerned here with repetition detection
			// so we don't want to overwrite it
			return;
		}
		if  ( (depthValue[index2] > 0) && (depthValue[index2] < depth2) && ( hash2== hashValue[index2])) {
			// don't overwrite if the bestmove is already in the table  resulting from a deeper search
			return;
		}
		stores++;
	    hashValue[index2] = hash2;
	    bestmove[index2]=move;
	    depthValue[index2]=depth2;
	    nodeScore[index2]=score;
	    
	    if(score > alpha) boundType |= LOWERBOUND; // if >= beta the true score could be higher, because we did ignore moves after cutoff
		if(score < beta)  boundType |= UPPERBOUND; // if <= alpha true score could be lower, because the daughters had beta cutoffs
		flags[index2] = boundType;                   // note that scores between alpha and beta are exact, so both upper and lower bound
	}
	public static boolean usefulScore(long hash2, int alpha, int beta)
	{
		int index2=index(hash2);
		int score2 = nodeScore[index2];
		int x = flags[index2] & LOWERBOUND;
		int y = flags[index2] & UPPERBOUND;
		
	  // scores above alpha must be lower bounds, or the current alpha is lower than the one used for calculating the score
	  if( score2 > alpha && ( x== 0)) return false;
	  // scores below beta must be upper bounds, or the current beta is larger than the one used for calculating the score
	  if( score2 < beta  && ( y == 0 )) return false;
	  usefulScores++;
	  return true; // if the score was exact, true upper bound <= alpha or true lower bound >= beta, it is meaningful
	}
}
